package com.zerro.litez.compiler.processor.file_builder;

import com.squareup.javapoet.*;
import com.zerro.litez.compiler.processor.parser.*;

import javax.lang.model.element.Modifier;
import java.util.ArrayList;
import java.util.List;

public class DatabaseImpBuilder extends FileBuilder {


    List<FieldSpec> daoInstancesList = new ArrayList<>();

    List<CodeBlock> getDaoInstanceList = new ArrayList<>();

    String nameStr = "name";

    public DatabaseImpBuilder() {
        super(null, "LitEzDatabase_Imp", null);

        onBuilding();
        finish();
    }

    @Override
    protected void onBuilding() {
        daoInstances();
        getClassBuilder().superclass(ClassName.get(runtimePackage, "LitEzDatabase"))
                .addFields(daoInstancesList)
                .addMethod(constructor())
                .addMethod(access());
    }

    private void daoInstances() {
        for (String daoName : DaoClassParser.getDaoClassParserMap().keySet()) {
            ClassName klass = ClassName.get(runtimePackage, "LitEzDao");
            DaoClassParser daoParser = DaoClassParser.getDaoClassParserMap().get(daoName);
            String daoNameLowerCase = daoName.toLowerCase();
            daoInstancesList.add(FieldSpec.builder(klass, daoNameLowerCase)
                    .initializer("new $T()", ClassName.get(packageName, daoName + "_Imp"))
                    .build());

            getDaoInstanceList.add(CodeBlock.builder()
                    .beginControlFlow("if ($L.equals($L.$L()))"
                            , nameStr, daoNameLowerCase, "__$$__getName")
                    .addStatement("$L.$L(supporter)", daoNameLowerCase, "__$$__setSupporter")
                    .addStatement("$L.$L($T.getFormatter($S))", daoNameLowerCase, "__$$__setFormatter",
                            ClassName.get(runtimePackage, "EntityFormatter"), daoParser.getEntity())
                    .addStatement("return (T) $L", daoNameLowerCase)
                    .endControlFlow()
                    .build());
        }
    }

    private MethodSpec constructor() {

        CodeBlock.Builder infoListCode = CodeBlock.builder();
        String tableInfoList = "tableInfoList";

        ClassName tableInfo = ClassName.get(runtimePackage + ".migration", "TableInfo");
        infoListCode.addStatement("$T $L = new $T<>()",
                ParameterizedTypeName.get(ClassName.get(List.class), tableInfo),
                tableInfoList, ArrayList.class);
        for (EntityClassParser parser : EntityClassParser.getEntityClassParserMap().values()) {

            String colSigns = getColSigns(parser);

            infoListCode.addStatement("$L.add(new $T($S,$S,$S))", tableInfoList, tableInfo,
                    parser.getSign(), parser.getCreateTableSQL(), colSigns);
            infoListCode.addStatement("new $T()", ClassName.get(packageName,
                    parser.getEntityName() + "_Formatter"));
        }
        return MethodSpec.constructorBuilder()
                .addModifiers(Modifier.PUBLIC)
                .addStatement("super()")
                .addCode(infoListCode.build())
                .addStatement("supporter.init($L)", tableInfoList)
                .build();
    }

    private String getColSigns(EntityClassParser parser) {
        StringBuilder colSigns = new StringBuilder();
        List<FieldInfo> fieldInfoList = parser.getAllInfoOfField(2);
        for (int i = 0; i < fieldInfoList.size(); i++) {
            FieldInfo fieldInfo = fieldInfoList.get(i);
            colSigns.append(fieldInfo.getSign());
            if (i < fieldInfoList.size() - 1) {
                colSigns.append(",");
            }
        }
        return colSigns.toString();
    }

    private MethodSpec access() {
        String dao = "dao";
        TypeVariableName genericsT = TypeVariableName.get("T");

        MethodSpec.Builder builder = MethodSpec.methodBuilder("access")
                .addModifiers(Modifier.PUBLIC)
                .addAnnotation(Override.class)
                .addParameter(ParameterizedTypeName
                        .get(ClassName.get(Class.class), genericsT), "dao")
                .addTypeVariable(genericsT)
                .returns(genericsT)
                .addStatement("$T $L = $L.getName()", String.class, nameStr, dao);

        for (CodeBlock codeBlock : getDaoInstanceList) {
            builder.addCode(codeBlock);
        }
        builder.addStatement("throw new $T(\"没有这样的 Dao \" + name)", RuntimeException.class);
        return builder.build();
    }

}
